package main

import (
	"fmt"
	"sync"
	"time"
)

//sync.WaitGroup
//利用Add增加计数
//Done减少计数
//Wait等待，直到计数为0
//也就是说，如果不Add或Add 0，则Wait不会阻塞
func main() {
	// demo1()
	// demo2()
	// demo3()
	demo4()
}

func demo1() {
	wg := sync.WaitGroup{}
	wg.Add(0)
	wg.Wait()
	fmt.Println("结束")
}

//不Add，直接Done
//直接panic
func demo3() {
	wg := sync.WaitGroup{}
	wg.Done() //panic: sync: negative WaitGroup counter
}

//正常使用
//Add数量与Done数量一致
func demo2() {
	wg := sync.WaitGroup{}
	wg.Add(3)

	go func() {
		time.Sleep(3 * time.Second)
		wg.Done()
	}()

	go func() {
		time.Sleep(2 * time.Second)
		wg.Done()
	}()

	go func() {
		time.Sleep(1 * time.Second)
		wg.Done()
	}()

	wg.Wait()
	fmt.Println("结束")
}

//非正常使用
//Add数量大于Done数量
//此时golang检测出已经没有相关协程在运行
//但是主线程在等待，因此死锁
func demo4() {
	wg := sync.WaitGroup{}

	wg.Add(2)
	go func() {
		time.Sleep(2 * time.Second)
		wg.Done()
	}()

	go func() {
		time.Sleep(1 * time.Second)
	}()

	fmt.Println("等待")
	wg.Wait()
}

//非正常使用
//Add数量大于Done数量
//但是依然存在执行中的协程
func demo5() {
	wg := sync.WaitGroup{}

	wg.Add(2)
	go func() {
		time.Sleep(2 * time.Second)
		wg.Done()
	}()

	go func() {
		time.Sleep(1 * time.Second)
	}()

	go func() {
		for {
			fmt.Println("协程执行") //由于存在继续执行的协程，因此不会死锁
			time.Sleep(1 * time.Second)
		}

	}()

	fmt.Println("等待")
	wg.Wait()
}
